package book.java8.char11;

import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.toList;

/**
 * @author MI
 * @version 1.0
 * @date 2020/12/19 17:16
 */
public class Shop {
    private final String shopName;

    public Shop(String name) {
        this.shopName = name;
    }

    static List<Shop> shopss = Arrays.asList(new Shop("BestPrice"), new Shop("LetsSaveBig"),
            new Shop("MyFavorteShop"), new Shop("BuyItAll")
    );

    public static List<String> findPircessss(String product) {
        return shopss.stream()
                .map(shop -> String.format("%s price is %.2f", shop.shopName, shop.getPrice(product)))
                .collect(Collectors.toList());
    }

    /**
     * 构造同步和异步操作
     * 1- 获取价格
     * <p>
     * <span>将lambda表达式作为参数传递给supplyAsync工厂方法就可以以异步方式对shop进行查询
     * 第一个转换的结果是一个 Stream<CompletableFuture<String>> ，一旦运行结束，每个
     * CompletableFuture对象中都会包含对应shop返回的字符串，
     * </span>
     * </p>
     * 2- 解析报价
     * <p>
     * <span>现在需要进行第二次转换字符串，将其转变为订单，将第一步生成的CompletableFuture
     * 对象调用它的thenApply，将一个字符串转换Quote的方法作为参数传递给他。
     * </span>
     * </p>
     * 3- 为计算折扣价格构造future
     * <p>
     * <span>
     * 第三个map操作涉及联系远程的Discount服务，为从商店中得到这一转换与前以转换又打不一样，
     * 因为这一转换需要远程执行，因此需要异步执行。
     * <p>
     * 为了实现这一目标，像第一个调用传递getPrice给supplyAsync那样，将这一操作以lambda
     * 表达式的方式传递给了supplyAsync工厂方法，该方法最终返回另一个CompletableFuture对象，
     * 到目前为止，已经进行了2次异步操作。用了两个不同的CompleTableFuture对象进行建模，你希望
     * 把他们以级联的方式串接起来进行工作。
     * a:从shop对象中获取价格，接着把价格转换为Quote.
     * b:拿到返回的Quote对象，将其作为参数传递Discount服务，取得最终的折扣价格。
     * <p>
     * thenCompose方法允许对两个异步操作进行流水线，第一个操作完成时，将其结果作为参数传递给第二个操作，
     * 换句话说，你可以创建两个CompleTableFutures对象，对第一个CompleTableFuture对象调用
     * thenCompose，并向其传递一个函数，当第一个CompleTableFuture执行完毕后，它的结果将作为
     * 该函数的参数，这个函数的返回值是以第一个CompletableFuture的返回作为第二个CompletableFuture对象
     * 。
     * </span>
     * </p>
     */
    public static List<String> findPirces(String product) {
        List<CompletableFuture<String>> priceFutures =
                shopss.stream()
                        .map(shop -> CompletableFuture.supplyAsync(//以异步方式取得  每个 shop 中指定  产品的原始价格
                                () -> shop.getPrice(product), executor))
                        .map(future -> future.thenApply(Quote::parse))
                        //使用另外一个异步任务构造期望的future申请折扣
                        .map(future -> future.thenCompose(quote ->
                                CompletableFuture.supplyAsync(
                                        () -> Discount.applyDiscount(quote), executor)))
                        .collect(toList());
        return priceFutures.stream().map(CompletableFuture::join).collect(toList());
    }

    public static List<String> findPircesFuture(String product) {
        return shopss.stream()
                .map(shop -> shop.getPrice(product))
                .map(Quote::parse)
                .map(Discount::applyDiscount)
                .collect(toList());
    }


    public static List<String> findPricess(String product) {
        final List<CompletableFuture<String>> collect = shopss.stream().map(shop ->
                CompletableFuture.supplyAsync(() -> String.format("%s price is %.2f", shop.shopName, shop.getPrice(product)))
        ).collect(toList());
        return collect.stream().map(CompletableFuture::join)//等待所有异步操作结束
                .collect(toList());
    }

    private final static Random random = new Random(System.currentTimeMillis());

    public double getPrices(String product) {
        delay();
        //待实现
        return 0.0;
    }

    public String getPrice(String product) {
        final double price = calculatePrice(product);
        final Discount.Code code = Discount.Code.values()[random.nextInt(Discount.Code.values().length)];
        return String.format("%s:%.2f:%s", shopName, price, code);

    }

    private double calculatePrice(String product) {
        delay();
        return random.nextDouble() * product.charAt(0) + product.charAt(1);

    }

    //将同步方法转为异步方法
    public CompletableFuture<Double> getPriceAsync(String product) {
        final CompletableFuture<Double> futurePrice = new CompletableFuture<>();

        new Thread(() -> {
            final double price = calcuatePrice(product);
            futurePrice.complete(price);
        }).start();
        return futurePrice;
    }

    private double calcuatePrice(String product) {
        delay();
        return random.nextDouble() * product.charAt(0) + product.charAt(1);
    }

    public static void delay() {
        try {
            Thread.sleep(1000L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {

        final long start = System.nanoTime();
        System.out.println(findPirces("myPhone"));
        final long duration = (System.nanoTime() - start) / 1_000_000;
        System.out.println("Done in " + duration + " msecs");

    }

    private final static Executor executor = Executors.newFixedThreadPool(Math.min(shopss.size(), 100),
            new ThreadFactory() {
                @Override
                public Thread newThread(Runnable r) {
                    final Thread thread = new Thread(r);
                    thread.setDaemon(true);//使用守护线程——这种方式不会阻止程序的关停
                    return thread;
                }
            }
    );


}
